import logging
from typing import Optional

from fastapi import HTTPException, Request
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint

from app.core.constants import PUBLIC_PATHS
from app.services.auth_service import AuthService

logger = logging.getLogger(__name__)
security = HTTPBearer()


class PermissionMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next: RequestResponseEndpoint):
        if request.url.path in PUBLIC_PATHS:
            return await call_next(request)

        try:
            credentials: Optional[HTTPAuthorizationCredentials] = await security(request)
            if not credentials:
                raise HTTPException(status_code=401, detail="未提供认证信息")

            user = await AuthService.get_user_by_token(credentials.credentials)
            if not user:
                raise HTTPException(status_code=401, detail="无效的认证信息或用户不存在")

            role_names = await AuthService.get_user_roles(user)
            method = request.method.lower()
            path = request.url.path

            api_resource = await AuthService.get_resource_by_path_method(path, method)
            if not api_resource:
                logger.warning(f"未找到API资源配置: {method} {path}")
                raise HTTPException(status_code=404, detail="资源不存在")

            allowed_role_names = await AuthService.get_resource_roles(api_resource)

            if not any(role in allowed_role_names for role in role_names):
                logger.warning(
                    f"用户权限不足: user_id={user.id}, "
                    f"roles={role_names}, "
                    f"required_roles={allowed_role_names}, "
                    f"path={path}"
                )
                raise HTTPException(status_code=403, detail="权限不足")

            request.state.user = user
            request.state.user_roles = role_names

            logger.info(
                f"访问授权成功: user_id={user.id}, " f"roles={role_names}, " f"method={method}, " f"path={path}"
            )

            return await call_next(request)

        except HTTPException as e:
            raise e
        except Exception as e:
            logger.error(f"权限检查发生错误: {str(e)}", exc_info=True)
            raise HTTPException(status_code=500, detail="内部服务器错误")
